package cn.nn200433.job.util;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.cron.pattern.parser.PatternParser;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSON;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.nn200433.job.config.AdminProperties;
import cn.nn200433.job.config.ExecutorProperties;
import cn.nn200433.job.config.XxlJobProperties;
import cn.nn200433.job.entity.Executor;
import cn.nn200433.job.entity.JobInfo;
import lombok.extern.slf4j.Slf4j;

import java.net.HttpCookie;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * xxl-job 远程操作工具类
 *
 * @author nn200433
 * @date 2024-03-01 03:03:22
 */
@Slf4j
public class XxlRemoteUtil {

    private static final String              COOKIE_XXL_JOB_LOGIN_IDENTITY       = "XXL_JOB_LOGIN_IDENTITY";
    private static final String              COOKIE_XXL_JOB_LOGIN_IDENTITY_PARAM = COOKIE_XXL_JOB_LOGIN_IDENTITY + "={}";
    private static final Map<String, String> LOGIN_COOKIE                        = new ConcurrentHashMap<String, String>();

    /**
     * 添加任务
     *
     * @param jobInfo 任务信息
     * @return {@link Integer } 任务ID
     * @author nn200433
     */
    public static Integer addJobInfo(JobInfo jobInfo) {
        if (StrUtil.isBlank(jobInfo.getScheduleType())) {
            jobInfo.setScheduleType("CRON");
        }
        if (StrUtil.isBlank(jobInfo.getGlueType())) {
            jobInfo.setGlueType("BEAN");
        }
        if (StrUtil.isBlank(jobInfo.getExecutorRouteStrategy())) {
            jobInfo.setExecutorRouteStrategy("FIRST");
        }
        if (StrUtil.isBlank(jobInfo.getMisfireStrategy())) {
            jobInfo.setMisfireStrategy("DO_NOTHING");
        }
        if (StrUtil.isBlank(jobInfo.getExecutorBlockStrategy())) {
            jobInfo.setExecutorBlockStrategy("SERIAL_EXECUTION");
        }
        PatternParser.parse(jobInfo.getScheduleConf());
        final String url = StrUtil.removeSuffix(getAdminCfg().getAddresses(), StrUtil.SLASH) + "/jobinfo/add";
        final HttpRequest request = HttpRequest.post(url)
                .form(BeanUtil.beanToMap(jobInfo))
                .cookie(getCookie());
        final HttpResponse response = request.execute();
        final JSON         json     = JSONUtil.parse(response.body());
        final Object       code     = json.getByPath("code");
        if (code.equals(200)) {
            return Convert.toInt(json.getByPath("content"));
        }
        throw new RuntimeException("add jobInfo error!");
    }

    /**
     * 更新任务
     *
     * @param jobInfo 任务信息
     * @return boolean
     * @author nn200433
     */
    public static boolean updateJobInfo(JobInfo jobInfo) {
        if (StrUtil.isBlank(jobInfo.getScheduleType())) {
            jobInfo.setScheduleType("CRON");
        }
        if (StrUtil.isBlank(jobInfo.getGlueType())) {
            jobInfo.setGlueType("BEAN");
        }
        if (StrUtil.isBlank(jobInfo.getExecutorRouteStrategy())) {
            jobInfo.setExecutorRouteStrategy("FIRST");
        }
        if (StrUtil.isBlank(jobInfo.getMisfireStrategy())) {
            jobInfo.setMisfireStrategy("DO_NOTHING");
        }
        if (StrUtil.isBlank(jobInfo.getExecutorBlockStrategy())) {
            jobInfo.setExecutorBlockStrategy("SERIAL_EXECUTION");
        }
        PatternParser.parse(jobInfo.getScheduleConf());
        final String url = StrUtil.removeSuffix(getAdminCfg().getAddresses(), StrUtil.SLASH) + "/jobinfo/update";
        final HttpRequest request = HttpRequest.post(url)
                .form(BeanUtil.beanToMap(jobInfo))
                .cookie(getCookie());
        final HttpResponse response = request.execute();
        final Object       code     = JSONUtil.parse(response.body()).getByPath("code");
        return code.equals(200);
    }

    /**
     * 启用
     *
     * @param jobId 作业id
     * @return boolean
     * @author nn200433
     */
    public static boolean start(Integer jobId) {
        final String url = StrUtil.removeSuffix(getAdminCfg().getAddresses(), StrUtil.SLASH) + "/jobinfo/start";
        HttpResponse response = HttpRequest.post(url)
                .form("id", jobId)
                .cookie(getCookie())
                .execute();
        final Object code = JSONUtil.parse(response.body()).getByPath("code");
        return code.equals(200);
    }

    /**
     * 停止
     *
     * @param jobId 作业id
     * @return boolean
     * @author nn200433
     */
    public static boolean stop(Integer jobId) {
        final String url = StrUtil.removeSuffix(getAdminCfg().getAddresses(), StrUtil.SLASH) + "/jobinfo/stop";
        HttpResponse response = HttpRequest.post(url)
                .form("id", jobId)
                .cookie(getCookie())
                .execute();
        final Object code = JSONUtil.parse(response.body()).getByPath("code");
        return code.equals(200);
    }

    /**
     * 获取工作信息
     *
     * @param jobGroupId      执行器ID
     * @param executorHandler 任务执行程序处理程序
     * @return {@link List }<{@link JobInfo }>
     * @author nn200433
     */
    public static List<JobInfo> getJobInfo(Integer jobGroupId, String executorHandler) {
        final String url = StrUtil.removeSuffix(getAdminCfg().getAddresses(), StrUtil.SLASH) + "/jobinfo/pageList";
        HttpResponse response = HttpRequest.post(url)
                .form("jobGroup", jobGroupId)
                .form("executorHandler", executorHandler)
                .form("triggerStatus", -1)
                .cookie(getCookie())
                .execute();
        final JSONArray array = JSONUtil.parse(response.body()).getByPath("data", JSONArray.class);
        return array.stream().map(o -> JSONUtil.toBean((JSONObject) o, JobInfo.class)).collect(Collectors.toList());
    }

    /**
     * 自动注册执行器
     *
     * @return boolean
     * @author nn200433
     */
    public static boolean autoRegisterExecutor() {
        String address = getExecutorCfg().getAddress();
        if (StrUtil.isBlank(address)) {
            address = "http://" + getExecutorCfg().getIp() + ":" + getExecutorCfg().getPort() + StrUtil.SLASH;
        }
        final String url = StrUtil.removeSuffix(getAdminCfg().getAddresses(), StrUtil.SLASH) + "/jobgroup/save";
        final HttpResponse response = HttpRequest.post(url)
                .form("appname", getExecutorCfg().getAppname())
                .form("title", getExecutorCfg().getAppname())
                .form("addressType", 1)
                .form("addressList", address)
                .cookie(getCookie())
                .execute();
        final Object code = JSONUtil.parse(response.body()).getByPath("code");
        return code.equals(200);
    }

    /**
     * 更新执行器
     *
     * @param id           主键
     * @param existAddress 已存在的地址
     * @return boolean
     * @author nn200433
     */
    public static boolean updateExecutor(Integer id, String existAddress) {
        String address = getExecutorCfg().getAddress();
        if (StrUtil.isBlank(address)) {
            address = "http://" + getExecutorCfg().getIp() + ":" + getExecutorCfg().getPort() + StrUtil.SLASH;
        }
        if (StrUtil.contains(existAddress, address)) {
            return Boolean.TRUE;
        }
        final String url = StrUtil.removeSuffix(getAdminCfg().getAddresses(), StrUtil.SLASH) + "/jobgroup/update";
        final HttpResponse response = HttpRequest.post(url)
                .form("appname", getExecutorCfg().getAppname())
                .form("title", getExecutorCfg().getAppname())
                .form("addressType", 1)
                .form("addressList", existAddress + StrUtil.COMMA + address)
                .form("id", id)
                .cookie(getCookie())
                .execute();
        final Object code = JSONUtil.parse(response.body()).getByPath("code");
        return code.equals(200);
    }

    /**
     * 精确检查
     *
     * @return {@link Executor }
     * @author nn200433
     */
    public static Executor preciselyCheck() {
        final List<Executor> executors = getExecutorList(null, null);
        return CollUtil.getFirst(executors);
    }

    /**
     * 获取执行器列表
     *
     * @param appName 应用程序名称
     * @param title   标题
     * @return {@link List }<{@link Executor }>
     * @author nn200433
     */
    public static List<Executor> getExecutorList(String appName, String title) {
        appName = StrUtil.blankToDefault(appName, getExecutorCfg().getAppname());
        final String url = StrUtil.removeSuffix(getAdminCfg().getAddresses(), StrUtil.SLASH) + "/jobgroup/pageList";
        final HttpResponse response = HttpRequest.post(url)
                .form("appname", appName)
                .form("title", title)
                .cookie(getCookie())
                .execute();
        final JSONArray array = JSONUtil.parse(response.body()).getByPath("data", JSONArray.class);
        return array.stream().map(o -> JSONUtil.toBean((JSONObject) o, Executor.class)).collect(Collectors.toList());
    }

    /**
     * 登录
     *
     * @author nn200433
     */
    private static void login() {
        final String url = StrUtil.removeSuffix(getAdminCfg().getAddresses(), StrUtil.SLASH) + "/login";
        final HttpResponse response = HttpRequest.post(url)
                .form("userName", getAdminCfg().getAccount())
                .form("password", getAdminCfg().getPassword())
                .execute();
        final HttpCookie cookie = response.getCookie(COOKIE_XXL_JOB_LOGIN_IDENTITY);
        Assert.notNull(cookie, "get xxl-job cookie error!");
        LOGIN_COOKIE.put(COOKIE_XXL_JOB_LOGIN_IDENTITY, cookie.getValue());
    }

    /**
     * 获取 Cookie
     *
     * @return {@link String }
     * @author nn200433
     */
    private static String getCookie() {
        for (int i = 0; i < 3; i++) {
            final String cookieStr = LOGIN_COOKIE.get(COOKIE_XXL_JOB_LOGIN_IDENTITY);
            if (cookieStr != null) {
                return StrUtil.format(COOKIE_XXL_JOB_LOGIN_IDENTITY_PARAM, cookieStr);
            }
            ThreadUtil.sleep(500);
            login();
        }
        throw new RuntimeException("get xxl-job cookie error!");
    }

    /**
     * 获取调度中心配置
     *
     * @return {@link XxlJobProperties }
     * @author nn200433
     */
    private static AdminProperties getAdminCfg() {
        return getProperties().getAdmin();
    }

    /**
     * 获取执行器配置
     *
     * @return {@link XxlJobProperties }
     * @author nn200433
     */
    private static ExecutorProperties getExecutorCfg() {
        return getProperties().getExecutor();
    }

    /**
     * 获取特性
     *
     * @return {@link XxlJobProperties }
     * @author nn200433
     */
    private static XxlJobProperties getProperties() {
        return SpringUtil.getBean(XxlJobProperties.class);
    }

}
